﻿using Modbus.Data;
using Modbus.Device;
using Modbus.Message;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.UI.WebControls.WebParts;
using System.Windows.Forms;
using static System.Windows.Forms.AxHost;

namespace PIMS.WorkPlugin.Util
{
    /// <summary>
    /// 使用NModbus4(2.1.0)库创建Modbus TCP Slave(服务器)
    /// Modbus Poll 及 Modbus Scan 可连
    /// </summary>
    internal class ModbusTcpSlaveHelper
    {
        private static TcpListener listener = null;
        private static ModbusTcpSlave slave = null;

        public static void Start(string ipAddr="127.0.0.1", string port ="502")
        {
            try
            {
                if(IsPortUsed(Convert.ToInt32(port)))
                {
                    throw new Exception($"端口号{port}被占用");
                }
                listener = new TcpListener(IPAddress.Parse(ipAddr), Convert.ToInt32(port));
                listener.Start();
                slave = ModbusTcpSlave.CreateTcp(1, listener);
                slave.DataStore = DataStoreFactory.CreateDefaultDataStore();
                //订阅数据到达事件，不能获取具体接收到的报文
                //slave.DataStore.DataStoreWrittenTo += DataStoreWrittenToHandle;
                //订阅接收到报文请求事件，可以打印接收到的报文
                slave.ModbusSlaveRequestReceived += ModbusSlaveRequestReceivedHandle;
                //订阅接收到写入完成事件，可以打印写入完成响应
                slave.WriteComplete += WriteCompleteHandle;
                slave.Listen();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public static void Stop() 
        {
            try
            {
                slave?.Dispose();
            }
            catch
            {

            }
        }

        private static void ModbusSlaveRequestReceivedHandle(object obj, ModbusSlaveRequestEventArgs e)
        {

            IModbusMessage message = e.Message;

            string str = "";
            foreach (var item in e.Message.MessageFrame)
            {
                str += item.ToString("x2").PadLeft(2, '0').ToUpper() + "  ";
            }
            Console.WriteLine(DateTime.Now.ToString() + $"功能码：{message.FunctionCode}, 服务器收到报文:  " + str);

        }

        private static void WriteCompleteHandle(object sender, ModbusSlaveRequestEventArgs e)
        {
            IModbusMessage message = e.Message;

            string str = "";
            foreach (var item in e.Message.MessageFrame)
            {
                str += item.ToString("x2").PadLeft(2, '0').ToUpper() + "  ";
            }
            Console.WriteLine(DateTime.Now.ToString() + $"功能码：{message.FunctionCode},  服务器写入完成:  " + str);

            switch(message.FunctionCode)
            {
                case 5:  //写Coil
                    break;
                case 6:  //写单个寄存器
                    break;
                case 16: //写多个寄存器
                    break;
            }
        }

        /// <summary>
        /// 读输出线圈(00001)
        /// </summary>
        /// <param name="addr"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static bool ReadCoilDiscretes(int addr)
        {
            if (slave != null)
            {
                return slave.DataStore.CoilDiscretes[addr];
            }
            throw new Exception("未初始化！");
        }

        /// <summary>
        /// 读保持型寄存器(40001)
        /// </summary>
        /// <param name="addr">从1开始</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static int ReadHoldingRegisters(int addr)
        {
            if (slave != null)
            {
                return slave.DataStore.HoldingRegisters[addr];
            }
            throw new Exception("未初始化！");
        }

        /// <summary>
        /// 写开关量输入(10001)
        /// </summary>
        /// <param name="addr"></param>
        /// <param name="value"></param>
        /// <exception cref="Exception"></exception>
        public static void WriteInputDiscretes(int addr, bool value)
        {
            if (listener != null && slave != null)
            {
                slave.DataStore.InputDiscretes[addr] = value;
                return;
            }
            throw new Exception("未初始化！");
        }

        /// <summary>
        /// 写输入型寄存器(30001)
        /// </summary>
        /// <param name="addr"></param>
        /// <param name="value"></param>
        /// <exception cref="Exception"></exception>
        public static void WriteInputRegisters(int addr, int value)
        {
            if (listener != null && slave != null)
            {
                slave.DataStore.InputRegisters[addr] = (ushort)(value);
                return;
            }
            throw new Exception("未初始化！");
        }

        /// <summary>
        /// 写保持型寄存器(40001)
        /// </summary>
        /// <param name="addr">从1开始</param>
        /// <param name="value"></param>
        public static void WriteHoldingRegisters(int addr,int value)
        {
            if (listener != null && slave != null)
            {
                slave.DataStore.HoldingRegisters[addr] = (ushort)(value);
                return;
            }
            throw new Exception("未初始化！");
        }

        /// <summary>
        /// 判断指定端口号是否被占用
        /// </summary>
        /// <param name="port"></param>
        /// <returns></returns>
        internal static Boolean IsPortUsed(Int32 port)
        {
            Boolean result = false;
            try
            {
                System.Net.NetworkInformation.IPGlobalProperties iproperties = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties();
                System.Net.IPEndPoint[] ipEndPoints = iproperties.GetActiveTcpListeners();
                //System.Net.NetworkInformation.TcpConnectionInformation[] conns = iproperties.GetActiveTcpConnections();

                //foreach (var con in conns)
                foreach (var con in ipEndPoints)
                {
                    // if (con.LocalEndPoint.Port == port)
                    if (con.Port == port)
                    {
                        result = true;
                        break;
                    }
                }
            }
            catch (Exception)
            {
            }
            return result;
        }
    }
}
